package org.nbict.iot.protocol.type;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.nbict.iot.protocol.Parser;
import org.nbict.iot.protocol.context.ParserContext;

import java.util.List;

/**
 * 报文域解析器
 * Created by songseven on 18/6/9.
 */
public enum FieldType implements Parser {

    /**
     * 对象类型报文域解析
     */
    OBJECT {
        public Object process(ParserContext context) {
            if (!checkPacket(context)) return null;

            ProtocolField field = context.field(); //获取报文域中的属性

            JSONObject out = new JSONObject();
            List<ProtocolField> properties = field.getProperties();
            //依次解析属性
            for (ProtocolField prop : properties) {
                //解析属性域
                context.field(prop);
                Object r = context.type().process(context);
                out.put(prop.getName(), r);
            }

            return out;
        }
    },

    /**
     * 数据类型报文域解析
     */
    ARRAY {
        public Object process(ParserContext context) {
            if (!checkPacket(context)) return null;

            ProtocolField field = context.field();
            JSONArray out = new JSONArray();

            //需要根据上下文计算获得
            String flowingNumExpression = field.getFlowingFieldNum();
            //获取数组内元素个数
            Integer flowingNum = context.getCacheByEL(flowingNumExpression);
            //获取数组内元素类型
            ProtocolField prop = field.getProperties().get(0);
            //依次解析
            for (int i = 0; i < flowingNum; i++) {
                context.field(prop); //设置数组内的域属性
                Object r = context.type().process(context);
                out.add(r);
            }

            return out;
        }
    },

    /**
     * 可变属性对象，一般用于原始报文长度变化的对象，例如报文体
     */
    DYNAMIC {
        public Object process(ParserContext context) {
            ProtocolField field = context.field();
            //获取可变属性对象的具体域
            int cmd = context.getCacheByEL(field.getTagExpression());
            if (cmd > field.getProperties().size()) return  null; //debug
            ProtocolField prop = field.getProperties().get(cmd - 1);

            //当前可变属性对象的报文范围
            int len = context.getCacheByEL(field.getPacketLenExpression());
            context.buffer().rangeReset(len);

            //获取别名域
            if (prop.getAliasTypeTag() > 0) {
                prop = field.getProperties().get(prop.getAliasTypeTag() - 1);
            }

            context.field(prop);
            Object out = context.type().process(context);

            return out;
        }
    },

    /**
     * 非固定属性对象数组类型报文域解析
     */
    DARRAY {
        public Object process(ParserContext context) {
            if (!checkPacket(context)) return null;

            ProtocolField field = context.field();

            JSONObject out = new JSONObject();

            //获取信息类型标识长度
            Integer tagLen = field.getFlowingTagLen();
            for (; context.buffer().inRange(); ) {
                //获取信息标识类型
                int tag = (int) FieldValueCast.INT.convert(context
                        .buffer().eat(tagLen), field);
                //获取标识类型对应的报文域
                if (tag > 0) {
                    ProtocolField prop = field.getProperties()
                            .get(tag - 1);
                    //获取相应的域对象
                    context.field(prop);
                    Object r = context.type().process(context);
                    out.put(prop.getName(), r);
                }
            }

            return out;
        }
    },

    /**
     * 字节类型报文域解析
     */
    BYTE {
        public Object process(ParserContext context) {
            if (!checkPacket(context)) return null;

            ProtocolField field = context.field();

            byte[] segment = context.buffer().eat(1);//默认是1
            context.buffer().skip(field.getFlowingPointer());

            Object out = segment;
            if (field.getConvertor() != null) {
                out = field.getConvertor().convert(segment, field);
                context.cache(out);
            }

            return out;
        }
    },

    /**
     * 字节数组类型报文域解析
     */
    BYTEN {
        public Object process(ParserContext context) {

            if (!checkPacket(context)) return null;

            ProtocolField field = context.field();
            int len = field.getFixedPacketLen();
            //如果是可变长度的字节数组
            if (len == 0) {
                String flowingNumExpression = field.getFlowingFieldNum();
                //获取数组内元素个数
                len = context.getCacheByEL(flowingNumExpression);
            }
            //截取对应的报文
            byte[] segment = context.buffer().eat(len);
            //跳过字节数
            context.buffer().skip(field.getFlowingPointer());

            Object out = segment;
            if (field.getConvertor() != null) {
                out = field.getConvertor().convert(segment, field);
                context.cache(out);
            }

            return out;
        }
    },

    /**
     * 无符号双整型报文域解析
     */
    INT {
        public Object process(ParserContext context) {
            if (!checkPacket(context)) return null;

            ProtocolField field = context.field();
            //截取对应的报文
            byte[] segment = context.buffer().eat(field.getFixedPacketLen());
            //跳过字节数
            context.buffer().skip(field.getFlowingPointer());
            Object out = field.getConvertor().convert(segment, field);
            //单位换算
            context.cache(out);

            return out;
        }
    },

    /**
     * 字符串报文域解析
     */
    STRING {
        public Object process(ParserContext context) {
            if (!checkPacket(context)) return null;

            ProtocolField field = context.field();
            //截取对应的报文
            byte[] segment = context.buffer().eat(field.getFixedPacketLen());
            //跳过字节数
            context.buffer().skip(field.getFlowingPointer());
            String out = (String)field.getConvertor().convert(segment, field);
            return out;
        }
    };

    /**
     * 检查当前报文长度是否合法
     *
     * @param context
     */
    public boolean checkPacket(ParserContext context) {
        return context.buffer().size() > 0;
    }
}
